{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "# 图增强RAG：基于图的检索增强生成\n",
        "\n",
        "在这个笔记本中，我实现了图增强RAG技术——这是一种通过将知识组织为连接图而不是平面文档集合来增强传统RAG系统的技术。这使得系统能够导航相关概念并检索比标准向量相似性方法更具上下文相关性的信息。\n",
        "\n",
        "## 图增强RAG的主要优势\n",
        "\n",
        "- 保持信息片段之间的关系\n",
        "- 通过连接概念的遍历来找到相关上下文\n",
        "- 改进对复杂、多部分查询的处理\n",
        "- 通过可视化知识路径提供更好的可解释性\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 设置环境\n",
        "我们首先导入必要的库。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [],
      "source": [
        "import os\n",
        "import numpy as np\n",
        "import json\n",
        "import fitz  # PyMuPDF\n",
        "from openai import OpenAI\n",
        "from typing import List, Dict, Tuple, Any\n",
        "import networkx as nx\n",
        "import matplotlib.pyplot as plt\n",
        "import heapq\n",
        "from collections import defaultdict\n",
        "import re\n",
        "from PIL import Image\n",
        "import io\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 设置OpenAI API客户端\n",
        "我们初始化OpenAI客户端来生成嵌入和响应。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 使用基础URL和API密钥初始化OpenAI客户端\n",
        "client = OpenAI(\n",
        "    base_url=\"http://localhost:11434/v1/\",\n",
        "    api_key=\"ollama\"  # Ollama不需要真实的API密钥，但客户端需要一个值\n",
        ")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 文档处理函数\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [],
      "source": [
        "def extract_text_from_pdf(pdf_path):\n",
        "    \"\"\"\n",
        "    从PDF文件中提取文本内容。\n",
        "    \n",
        "    参数:\n",
        "        pdf_path (str): PDF文件的路径\n",
        "        \n",
        "    返回:\n",
        "        str: 提取的文本内容\n",
        "    \"\"\"\n",
        "    print(f\"正在从 {pdf_path} 提取文本...\")  # 打印正在处理的PDF路径\n",
        "    pdf_document = fitz.open(pdf_path)  # 使用PyMuPDF打开PDF文件\n",
        "    text = \"\"  # 初始化空字符串来存储提取的文本\n",
        "    \n",
        "    # 遍历PDF中的每一页\n",
        "    for page_num in range(pdf_document.page_count):\n",
        "        page = pdf_document[page_num]  # 获取页面对象\n",
        "        text += page.get_text()  # 从页面提取文本并追加到文本字符串\n",
        "    \n",
        "    return text  # 返回提取的文本内容\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [],
      "source": [
        "def chunk_text(text, chunk_size=1000, overlap=200):\n",
        "    \"\"\"\n",
        "    将文本分割为重叠的文本块。\n",
        "    \n",
        "    参数:\n",
        "        text (str): 要分块的输入文本\n",
        "        chunk_size (int): 每个块的字符大小\n",
        "        overlap (int): 块之间的重叠字符数\n",
        "        \n",
        "    返回:\n",
        "        List[Dict]: 包含元数据的块列表\n",
        "    \"\"\"\n",
        "    chunks = []  # 初始化空列表来存储块\n",
        "    \n",
        "    # 以(chunk_size - overlap)的步长遍历文本\n",
        "    for i in range(0, len(text), chunk_size - overlap):\n",
        "        # 从当前位置提取文本块\n",
        "        chunk_text = text[i:i + chunk_size]\n",
        "        \n",
        "        # 确保不添加空块\n",
        "        if chunk_text:\n",
        "            # 将块及其元数据添加到列表中\n",
        "            chunks.append({\n",
        "                \"text\": chunk_text,  # 文本块\n",
        "                \"index\": len(chunks),  # 块的索引\n",
        "                \"start_pos\": i,  # 块在原文本中的起始位置\n",
        "                \"end_pos\": i + len(chunk_text)  # 块在原文本中的结束位置\n",
        "            })\n",
        "    \n",
        "    # 打印创建的块数量\n",
        "    print(f\"创建了 {len(chunks)} 个文本块\")\n",
        "    \n",
        "    return chunks  # 返回块列表\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 创建嵌入\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [],
      "source": [
        "def create_embeddings(texts, model=\"bge-m3:latest\"):\n",
        "    \"\"\"\n",
        "    为给定文本创建嵌入。\n",
        "    \n",
        "    参数:\n",
        "        texts (List[str]): 输入文本\n",
        "        model (str): 嵌入模型名称\n",
        "        \n",
        "    返回:\n",
        "        List[List[float]]: 嵌入向量\n",
        "    \"\"\"\n",
        "    # 处理空输入\n",
        "    if not texts:\n",
        "        return []\n",
        "        \n",
        "    # 如果需要，按批次处理（OpenAI API限制）\n",
        "    batch_size = 100\n",
        "    all_embeddings = []\n",
        "    \n",
        "    # 按批次遍历输入文本\n",
        "    for i in range(0, len(texts), batch_size):\n",
        "        batch = texts[i:i + batch_size]  # 获取当前批次的文本\n",
        "        \n",
        "        # 为当前批次创建嵌入\n",
        "        response = client.embeddings.create(\n",
        "            model=model,\n",
        "            input=batch\n",
        "        )\n",
        "        \n",
        "        # 从响应中提取嵌入\n",
        "        batch_embeddings = [item.embedding for item in response.data]\n",
        "        all_embeddings.extend(batch_embeddings)  # 将批次嵌入添加到列表中\n",
        "    \n",
        "    return all_embeddings  # 返回所有嵌入\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 知识图谱构建\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [],
      "source": [
        "def extract_concepts(text):\n",
        "    \"\"\"\n",
        "    使用OpenAI的API从文本中提取关键概念。\n",
        "    \n",
        "    参数:\n",
        "        text (str): 要提取概念的文本\n",
        "        \n",
        "    返回:\n",
        "        List[str]: 概念列表\n",
        "    \"\"\"\n",
        "    # 系统消息指导模型执行任务\n",
        "    system_message = \"\"\"从提供的文本中提取关键概念和实体。\n",
        "仅返回5-10个在此文本中最重要的关键术语、实体或概念的列表。\n",
        "将你的响应格式化为字符串的JSON数组。\"\"\"\n",
        "\n",
        "    # 向OpenAI API发出请求\n",
        "    response = client.chat.completions.create(\n",
        "        model=\"qwen2.5:7b\",\n",
        "        messages=[\n",
        "            {\"role\": \"system\", \"content\": system_message},\n",
        "            {\"role\": \"user\", \"content\": f\"从以下文本中提取关键概念:\\n\\n{text[:3000]}\"}  # API限制\n",
        "        ],\n",
        "        temperature=0.0,\n",
        "        response_format={\"type\": \"json_object\"}\n",
        "    )\n",
        "    \n",
        "    try:\n",
        "        # 从响应中解析概念\n",
        "        concepts_json = json.loads(response.choices[0].message.content)\n",
        "        concepts = concepts_json.get(\"concepts\", [])\n",
        "        if not concepts and \"concepts\" not in concepts_json:\n",
        "            # 尝试获取响应中的任何数组\n",
        "            for key, value in concepts_json.items():\n",
        "                if isinstance(value, list):\n",
        "                    concepts = value\n",
        "                    break\n",
        "        return concepts\n",
        "    except (json.JSONDecodeError, AttributeError):\n",
        "        # 如果JSON解析失败的后备方案\n",
        "        content = response.choices[0].message.content\n",
        "        # 尝试提取看起来像列表的任何内容\n",
        "        matches = re.findall(r'\\[(.*?)\\]', content, re.DOTALL)\n",
        "        if matches:\n",
        "            items = re.findall(r'\"([^\"]*)\"', matches[0])\n",
        "            return items\n",
        "        return []\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [],
      "source": [
        "def build_knowledge_graph(chunks):\n",
        "    \"\"\"\n",
        "    从文本块构建知识图谱。\n",
        "    \n",
        "    参数:\n",
        "        chunks (List[Dict]): 包含元数据的文本块列表\n",
        "        \n",
        "    返回:\n",
        "        Tuple[nx.Graph, List[np.ndarray]]: 知识图谱和块嵌入\n",
        "    \"\"\"\n",
        "    print(\"正在构建知识图谱...\")\n",
        "    \n",
        "    # 创建图\n",
        "    graph = nx.Graph()\n",
        "    \n",
        "    # 提取块文本\n",
        "    texts = [chunk[\"text\"] for chunk in chunks]\n",
        "    \n",
        "    # 为所有块创建嵌入\n",
        "    print(\"正在为块创建嵌入...\")\n",
        "    embeddings = create_embeddings(texts)\n",
        "    \n",
        "    # 向图中添加节点\n",
        "    print(\"正在向图中添加节点...\")\n",
        "    for i, chunk in enumerate(chunks):\n",
        "        # 从块中提取概念\n",
        "        print(f\"正在为块 {i+1}/{len(chunks)} 提取概念...\")\n",
        "        concepts = extract_concepts(chunk[\"text\"])\n",
        "        \n",
        "        # 添加带属性的节点\n",
        "        graph.add_node(i, \n",
        "                      text=chunk[\"text\"], \n",
        "                      concepts=concepts,\n",
        "                      embedding=embeddings[i])\n",
        "    \n",
        "    # 基于共享概念连接节点\n",
        "    print(\"正在创建节点间的边...\")\n",
        "    for i in range(len(chunks)):\n",
        "        node_concepts = set(graph.nodes[i][\"concepts\"])\n",
        "        \n",
        "        for j in range(i + 1, len(chunks)):\n",
        "            # 计算概念重叠\n",
        "            other_concepts = set(graph.nodes[j][\"concepts\"])\n",
        "            shared_concepts = node_concepts.intersection(other_concepts)\n",
        "            \n",
        "            # 如果它们共享概念，添加边\n",
        "            if shared_concepts:\n",
        "                # 使用嵌入计算语义相似性\n",
        "                similarity = np.dot(embeddings[i], embeddings[j]) / (np.linalg.norm(embeddings[i]) * np.linalg.norm(embeddings[j]))\n",
        "                \n",
        "                # 基于概念重叠和语义相似性计算边权重\n",
        "                concept_score = len(shared_concepts) / min(len(node_concepts), len(other_concepts))\n",
        "                edge_weight = 0.7 * similarity + 0.3 * concept_score\n",
        "                \n",
        "                # 只添加具有显著关系的边\n",
        "                if edge_weight > 0.6:\n",
        "                    graph.add_edge(i, j, \n",
        "                                  weight=edge_weight,\n",
        "                                  similarity=similarity,\n",
        "                                  shared_concepts=list(shared_concepts))\n",
        "    \n",
        "    print(f\"构建了包含 {graph.number_of_nodes()} 个节点和 {graph.number_of_edges()} 条边的知识图谱\")\n",
        "    return graph, embeddings\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 图遍历和查询处理\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {},
      "outputs": [],
      "source": [
        "def traverse_graph(query, graph, embeddings, top_k=5, max_depth=3):\n",
        "    \"\"\"\n",
        "    遍历知识图谱以找到查询的相关信息。\n",
        "    \n",
        "    参数:\n",
        "        query (str): 用户的问题\n",
        "        graph (nx.Graph): 知识图谱\n",
        "        embeddings (List): 节点嵌入列表\n",
        "        top_k (int): 考虑的初始节点数量\n",
        "        max_depth (int): 最大遍历深度\n",
        "        \n",
        "    返回:\n",
        "        List[Dict]: 从图遍历获得的相关信息\n",
        "    \"\"\"\n",
        "    print(f\"正在为查询遍历图: {query}\")\n",
        "    \n",
        "    # 获取查询嵌入\n",
        "    query_embedding = create_embeddings([query])[0]\n",
        "    \n",
        "    # 计算查询与所有节点之间的相似性\n",
        "    similarities = []\n",
        "    for i, node_embedding in enumerate(embeddings):\n",
        "        similarity = np.dot(query_embedding, node_embedding) / (np.linalg.norm(query_embedding) * np.linalg.norm(node_embedding))\n",
        "        similarities.append((i, similarity))\n",
        "    \n",
        "    # 按相似性排序（降序）\n",
        "    similarities.sort(key=lambda x: x[1], reverse=True)\n",
        "    \n",
        "    # 获取top-k最相似的节点作为起始点\n",
        "    starting_nodes = [node for node, _ in similarities[:top_k]]\n",
        "    print(f\"从 {len(starting_nodes)} 个节点开始遍历\")\n",
        "    \n",
        "    # 初始化遍历\n",
        "    visited = set()  # 用于跟踪访问过的节点的集合\n",
        "    traversal_path = []  # 存储遍历路径的列表\n",
        "    results = []  # 存储结果的列表\n",
        "    \n",
        "    # 使用优先队列进行遍历\n",
        "    queue = []\n",
        "    for node in starting_nodes:\n",
        "        heapq.heappush(queue, (-similarities[node][1], node))  # 负数用于最大堆\n",
        "    \n",
        "    # 使用带优先级的修改广度优先搜索遍历图\n",
        "    while queue and len(results) < (top_k * 3):  # 限制结果为top_k * 3\n",
        "        _, node = heapq.heappop(queue)\n",
        "        \n",
        "        if node in visited:\n",
        "            continue\n",
        "        \n",
        "        # 标记为已访问\n",
        "        visited.add(node)\n",
        "        traversal_path.append(node)\n",
        "        \n",
        "        # 将当前节点的文本添加到结果中\n",
        "        results.append({\n",
        "            \"text\": graph.nodes[node][\"text\"],\n",
        "            \"concepts\": graph.nodes[node][\"concepts\"],\n",
        "            \"node_id\": node\n",
        "        })\n",
        "        \n",
        "        # 如果我们还没有达到最大深度，探索邻居\n",
        "        if len(traversal_path) < max_depth:\n",
        "            neighbors = [(neighbor, graph[node][neighbor][\"weight\"]) \n",
        "                        for neighbor in graph.neighbors(node)\n",
        "                        if neighbor not in visited]\n",
        "            \n",
        "            # 基于边权重将邻居添加到队列中\n",
        "            for neighbor, weight in sorted(neighbors, key=lambda x: x[1], reverse=True):\n",
        "                heapq.heappush(queue, (-weight, neighbor))\n",
        "    \n",
        "    print(f\"图遍历找到了 {len(results)} 个相关块\")\n",
        "    return results, traversal_path\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 响应生成\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {},
      "outputs": [],
      "source": [
        "def generate_response(query, context_chunks):\n",
        "    \"\"\"\n",
        "    使用检索到的上下文生成响应。\n",
        "    \n",
        "    参数:\n",
        "        query (str): 用户的问题\n",
        "        context_chunks (List[Dict]): 图遍历得到的相关块\n",
        "        \n",
        "    返回:\n",
        "        str: 生成的响应\n",
        "    \"\"\"\n",
        "    # 从上下文中的每个块提取文本\n",
        "    context_texts = [chunk[\"text\"] for chunk in context_chunks]\n",
        "    \n",
        "    # 将提取的文本合并为单个上下文字符串，用\"---\"分隔\n",
        "    combined_context = \"\\n\\n---\\n\\n\".join(context_texts)\n",
        "    \n",
        "    # 定义上下文的最大允许长度（OpenAI限制）\n",
        "    max_context = 14000\n",
        "    \n",
        "    # 如果合并的上下文超过最大长度，则截断\n",
        "    if len(combined_context) > max_context:\n",
        "        combined_context = combined_context[:max_context] + \"... [已截断]\"\n",
        "    \n",
        "    # 定义系统消息来指导AI助手\n",
        "    system_message = \"\"\"你是一个有用的AI助手。基于提供的上下文回答用户的问题。\n",
        "如果信息不在上下文中，请说出来。在回答中尽可能引用上下文的特定部分。\"\"\"\n",
        "\n",
        "    # 使用OpenAI API生成响应\n",
        "    response = client.chat.completions.create(\n",
        "        model=\"qwen2.5:7b\",  # 指定要使用的模型\n",
        "        messages=[\n",
        "            {\"role\": \"system\", \"content\": system_message},  # 指导助手的系统消息\n",
        "            {\"role\": \"user\", \"content\": f\"上下文:\\n{combined_context}\\n\\n问题: {query}\"}  # 包含上下文和查询的用户消息\n",
        "        ],\n",
        "        temperature=0.2  # 设置响应生成的温度\n",
        "    )\n",
        "    \n",
        "    # 返回生成的响应内容\n",
        "    return response.choices[0].message.content\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 可视化和完整流水线\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {},
      "outputs": [],
      "source": [
        "def visualize_graph_traversal(graph, traversal_path):\n",
        "    \"\"\"\n",
        "    可视化知识图谱和遍历路径。\n",
        "    \n",
        "    参数:\n",
        "        graph (nx.Graph): 知识图谱\n",
        "        traversal_path (List): 遍历顺序的节点列表\n",
        "    \"\"\"\n",
        "    plt.figure(figsize=(12, 10))  # 设置图形大小\n",
        "    \n",
        "    # 定义节点颜色，默认为浅蓝色\n",
        "    node_color = ['lightblue'] * graph.number_of_nodes()\n",
        "    \n",
        "    # 将遍历路径中的节点高亮为浅绿色\n",
        "    for node in traversal_path:\n",
        "        node_color[node] = 'lightgreen'\n",
        "    \n",
        "    # 将起始节点高亮为绿色，结束节点高亮为红色\n",
        "    if traversal_path:\n",
        "        node_color[traversal_path[0]] = 'green'\n",
        "        node_color[traversal_path[-1]] = 'red'\n",
        "    \n",
        "    # 使用spring布局为所有节点创建位置\n",
        "    pos = nx.spring_layout(graph, k=0.5, iterations=50, seed=42)\n",
        "    \n",
        "    # 绘制图节点\n",
        "    nx.draw_networkx_nodes(graph, pos, node_color=node_color, node_size=500, alpha=0.8)\n",
        "    \n",
        "    # 绘制边，宽度与权重成正比\n",
        "    for u, v, data in graph.edges(data=True):\n",
        "        weight = data.get('weight', 1.0)\n",
        "        nx.draw_networkx_edges(graph, pos, edgelist=[(u, v)], width=weight*2, alpha=0.6)\n",
        "    \n",
        "    # 用红色虚线绘制遍历路径\n",
        "    traversal_edges = [(traversal_path[i], traversal_path[i+1]) \n",
        "                      for i in range(len(traversal_path)-1)]\n",
        "    \n",
        "    nx.draw_networkx_edges(graph, pos, edgelist=traversal_edges, \n",
        "                          width=3, alpha=0.8, edge_color='red', \n",
        "                          style='dashed', arrows=True)\n",
        "    \n",
        "    # 为每个节点添加第一个概念的标签\n",
        "    labels = {}\n",
        "    for node in graph.nodes():\n",
        "        concepts = graph.nodes[node]['concepts']\n",
        "        label = concepts[0] if concepts else f\"节点 {node}\"\n",
        "        labels[node] = f\"{node}: {label}\"\n",
        "    \n",
        "    nx.draw_networkx_labels(graph, pos, labels=labels, font_size=8)\n",
        "    \n",
        "    plt.title(\"知识图谱与遍历路径\")  # 设置图表题目\n",
        "    plt.axis('off')  # 关闭坐标轴\n",
        "    plt.tight_layout()  # 调整布局\n",
        "    plt.show()  # 显示图表\n",
        "\n",
        "def graph_rag_pipeline(pdf_path, query, chunk_size=1000, chunk_overlap=200, top_k=3):\n",
        "    \"\"\"\n",
        "    从文档到答案的完整图增强RAG流水线。\n",
        "    \n",
        "    参数:\n",
        "        pdf_path (str): PDF文档路径\n",
        "        query (str): 用户的问题\n",
        "        chunk_size (int): 文本块大小\n",
        "        chunk_overlap (int): 块之间的重叠\n",
        "        top_k (int): 遍历考虑的顶级节点数量\n",
        "        \n",
        "    返回:\n",
        "        Dict: 包括答案和图可视化数据的结果\n",
        "    \"\"\"\n",
        "    # 从PDF文档中提取文本\n",
        "    text = extract_text_from_pdf(pdf_path)\n",
        "    \n",
        "    # 将提取的文本分割为重叠的块\n",
        "    chunks = chunk_text(text, chunk_size, chunk_overlap)\n",
        "    \n",
        "    # 从文本块构建知识图谱\n",
        "    graph, embeddings = build_knowledge_graph(chunks)\n",
        "    \n",
        "    # 遍历知识图谱以找到查询的相关信息\n",
        "    relevant_chunks, traversal_path = traverse_graph(query, graph, embeddings, top_k)\n",
        "    \n",
        "    # 基于查询和相关块生成响应\n",
        "    response = generate_response(query, relevant_chunks)\n",
        "    \n",
        "    # 可视化图遍历路径\n",
        "    visualize_graph_traversal(graph, traversal_path)\n",
        "    \n",
        "    # 返回查询、响应、相关块、遍历路径和图\n",
        "    return {\n",
        "        \"query\": query,\n",
        "        \"response\": response,\n",
        "        \"relevant_chunks\": relevant_chunks,\n",
        "        \"traversal_path\": traversal_path,\n",
        "        \"graph\": graph\n",
        "    }\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## 在样本PDF文档上评估图增强RAG\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "正在从 data/AI_Information_zh.pdf 提取文本...\n",
            "创建了 13 个文本块\n",
            "正在构建知识图谱...\n",
            "正在为块创建嵌入...\n",
            "正在向图中添加节点...\n",
            "正在为块 1/13 提取概念...\n",
            "正在为块 2/13 提取概念...\n",
            "正在为块 3/13 提取概念...\n",
            "正在为块 4/13 提取概念...\n",
            "正在为块 5/13 提取概念...\n",
            "正在为块 6/13 提取概念...\n",
            "正在为块 7/13 提取概念...\n",
            "正在为块 8/13 提取概念...\n",
            "正在为块 9/13 提取概念...\n",
            "正在为块 10/13 提取概念...\n",
            "正在为块 11/13 提取概念...\n",
            "正在为块 12/13 提取概念...\n",
            "正在为块 13/13 提取概念...\n",
            "正在创建节点间的边...\n",
            "构建了包含 13 个节点和 1 条边的知识图谱\n",
            "正在为查询遍历图: 变换器在自然语言处理中的关键应用是什么？\n",
            "从 3 个节点开始遍历\n",
            "图遍历找到了 3 个相关块\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 30693 (\\N{CJK UNIFIED IDEOGRAPH-77E5}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 35782 (\\N{CJK UNIFIED IDEOGRAPH-8BC6}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 22270 (\\N{CJK UNIFIED IDEOGRAPH-56FE}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 35889 (\\N{CJK UNIFIED IDEOGRAPH-8C31}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 19982 (\\N{CJK UNIFIED IDEOGRAPH-4E0E}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 36941 (\\N{CJK UNIFIED IDEOGRAPH-904D}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 21382 (\\N{CJK UNIFIED IDEOGRAPH-5386}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 36335 (\\N{CJK UNIFIED IDEOGRAPH-8DEF}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/var/folders/bw/c91xbm4d4ds65s3zg_01xz600000gn/T/ipykernel_40389/2510042062.py:53: UserWarning: Glyph 24452 (\\N{CJK UNIFIED IDEOGRAPH-5F84}) missing from font(s) DejaVu Sans.\n",
            "  plt.tight_layout()  # 调整布局\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20154 (\\N{CJK UNIFIED IDEOGRAPH-4EBA}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24037 (\\N{CJK UNIFIED IDEOGRAPH-5DE5}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 26234 (\\N{CJK UNIFIED IDEOGRAPH-667A}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 33021 (\\N{CJK UNIFIED IDEOGRAPH-80FD}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24378 (\\N{CJK UNIFIED IDEOGRAPH-5F3A}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 21270 (\\N{CJK UNIFIED IDEOGRAPH-5316}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 23398 (\\N{CJK UNIFIED IDEOGRAPH-5B66}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20064 (\\N{CJK UNIFIED IDEOGRAPH-4E60}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 27450 (\\N{CJK UNIFIED IDEOGRAPH-6B3A}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 35784 (\\N{CJK UNIFIED IDEOGRAPH-8BC8}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 26816 (\\N{CJK UNIFIED IDEOGRAPH-68C0}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 27979 (\\N{CJK UNIFIED IDEOGRAPH-6D4B}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20316 (\\N{CJK UNIFIED IDEOGRAPH-4F5C}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 23703 (\\N{CJK UNIFIED IDEOGRAPH-5C97}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20301 (\\N{CJK UNIFIED IDEOGRAPH-4F4D}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 36716 (\\N{CJK UNIFIED IDEOGRAPH-8F6C}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 31227 (\\N{CJK UNIFIED IDEOGRAPH-79FB}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 29289 (\\N{CJK UNIFIED IDEOGRAPH-7269}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20307 (\\N{CJK UNIFIED IDEOGRAPH-4F53}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 35782 (\\N{CJK UNIFIED IDEOGRAPH-8BC6}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 21035 (\\N{CJK UNIFIED IDEOGRAPH-522B}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 33258 (\\N{CJK UNIFIED IDEOGRAPH-81EA}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 21160 (\\N{CJK UNIFIED IDEOGRAPH-52A8}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 33410 (\\N{CJK UNIFIED IDEOGRAPH-8282}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 28857 (\\N{CJK UNIFIED IDEOGRAPH-70B9}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24935 (\\N{CJK UNIFIED IDEOGRAPH-6167}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 22478 (\\N{CJK UNIFIED IDEOGRAPH-57CE}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24066 (\\N{CJK UNIFIED IDEOGRAPH-5E02}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20132 (\\N{CJK UNIFIED IDEOGRAPH-4EA4}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20114 (\\N{CJK UNIFIED IDEOGRAPH-4E92}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24335 (\\N{CJK UNIFIED IDEOGRAPH-5F0F}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 38899 (\\N{CJK UNIFIED IDEOGRAPH-97F3}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 20048 (\\N{CJK UNIFIED IDEOGRAPH-4E50}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 39564 (\\N{CJK UNIFIED IDEOGRAPH-9A8C}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24179 (\\N{CJK UNIFIED IDEOGRAPH-5E73}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 34913 (\\N{CJK UNIFIED IDEOGRAPH-8861}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 30693 (\\N{CJK UNIFIED IDEOGRAPH-77E5}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 22270 (\\N{CJK UNIFIED IDEOGRAPH-56FE}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 35889 (\\N{CJK UNIFIED IDEOGRAPH-8C31}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 19982 (\\N{CJK UNIFIED IDEOGRAPH-4E0E}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 36941 (\\N{CJK UNIFIED IDEOGRAPH-904D}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 21382 (\\N{CJK UNIFIED IDEOGRAPH-5386}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 36335 (\\N{CJK UNIFIED IDEOGRAPH-8DEF}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n",
            "/opt/anaconda3/envs/rag/lib/python3.12/site-packages/IPython/core/pylabtools.py:170: UserWarning: Glyph 24452 (\\N{CJK UNIFIED IDEOGRAPH-5F84}) missing from font(s) DejaVu Sans.\n",
            "  fig.canvas.print_figure(bytes_io, **kw)\n"
          ]
        },
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAPdCAYAAABba9tpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAk09JREFUeJzs3XmcnWV9///3fWZPMklISICwI0uABAErIiLaRWu1bq37ilZxqXtbbat16bd2tbZVf61WXHCpSsUF961S6y4KEjZZE5aEBLIns59z//64nUwCWWaSmTOZmefTRx6ZOefMfa4RHMyL6/rcRVmWZQAAAACgiWqTvQAAAAAAZh5RCgAAAICmE6UAAAAAaDpRCgAAAICmE6UAAAAAaDpRCgAAAICmE6UAAAAAaDpRCgAAAICmE6UAAAAAaDpRCgAAAICmE6UAAAAAaLrWyV4AAMDOrrvuupx11llpb2/f7fMDAwO56qqr9vmaG264IX19fV63m9c96EEP2u3zAADNJEoBAAeVsixzzjnn5Pvf//5unz/33HNH/Rqv2/3rAAAOBo7vAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATdc62QsAALi/H//4x5k/f/5un9u2bduoX+N1e34dAMBkK8qyLCd7EQAAAADMLI7vAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATSdKAQAAANB0ohQAAAAATdc62QsAABhPPYM9+b9V/5fr7r0uK9auyHX3Xpee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MubMmZPnPOc5aTQaueSSSzI0NDTZS2I/iVIAAADAlHL22Wfn7LPPzt13352vfe1rk70c9pMoBQAAAEw5z372szN79ux89atfzV133TXZy2E/iFIAAADAlDN37tw861nPSqPRyEc/+tHU6/XJXhJjJEoBAAAAU9JDH/rQPPjBD86dd96Zb37zm5O9HMZIlAIAAACmpKIo8pznPCezZs3Kl7/85axevXqyl8QYiFIAAADAlDV//vw8/elPz9DQUC655JI0Go3JXhKjJEoBAAAAU9rDH/7wnH766Vm5cmW+/e1vT/ZyGCVRCgAAAJjSiqLI85///HR2dubyyy/P2rVrJ3tJjIIoBQAAAEx5hxxySJ72tKdlcHDQMb4pQpQCAAAApoXzzz8/S5cuza233prvfve7k70c9kGUAgAAAKaF4WN8HR0d+fznP5977713spfEXohSAAAAwLRx6KGH5qlPfWoGBwfzsY99LGVZTvaS2ANRCgAAAJhWHv3oR+ekk07KTTfdlO9973uTvRz2QJQCAAAAppWiKPKCF7wgbW1tueyyy7J+/frJXhK7IUoBAAAA087ixYvzlKc8Jf39/fn4xz/uGN9BSJQCAAAApqXf+q3fygknnJAbbrghP/zhDyd7OdyPKAUAAABMS7VaLS984QvT2tqaSy+9NJs2bZrsJbETUQoAAACYtg4//PA88YlPTF9fXz7xiU84xncQEaUAAACAae2xj31sjj322KxYsSI/+clPJns5/JooBQAAAExrtVotF154YVpaWvKZz3wmmzdvnuwlEVEKAAAAmAGWLFmSJzzhCenp6cl//dd/OcZ3EBClAAAAgBnhcY97XI466qhcffXV+fnPfz7Zy5nxRCkAAABgRmhpacmFF16YWq2WT33qU9m6detkL2lGE6UAAACAGePoo4/O4x73uGzbti2f/vSnJ3s5M5ooBQAAAMwoT3jCE7JkyZJceeWVueqqqyZ7OTOWKAUAAADMKK2trXnhC1+YoijyX//1X9m+fftkL2lGEqUAAACAGee4447LYx/72GzZsiWXXnrpZC9nRhKlAAAAgBnpiU98Yg477LD8+Mc/zooVKyZ7OTOOKAUAAADMSG1tbTuO8X3iE59IT0/PZC9pRhGlAAAAgBnrQQ96UH7rt34rmzZtymc/+9nJXs6MIkoBAAAAM9pTnvKULFq0KD/4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SgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0nSgEAAADQdKIUAAAAAE0n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",
            "text/plain": [
              "<Figure size 1200x1000 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "=== 答案 ===\n",
            "很抱歉，但上下文中并没有提到“变换器”（Transformer）这个词，也没有直接讨论其在自然语言处理中的应用。根据提供的信息，该章节主要介绍了人工智能的基本概念、未来趋势以及与机器人技术的融合等内容，并没有涉及具体的自然语言处理技术或模型。\n",
            "\n",
            "如果您有关于人工智能或其他相关主题的问题，可以随时提问！\n"
          ]
        }
      ],
      "source": [
        "# 包含AI信息的PDF文档路径\n",
        "pdf_path = \"data/AI_Information_zh.pdf\"\n",
        "\n",
        "# 定义一个AI相关查询来测试图增强RAG\n",
        "query = \"变换器在自然语言处理中的关键应用是什么？\"\n",
        "\n",
        "# 执行图增强RAG流水线来处理文档并回答查询\n",
        "results = graph_rag_pipeline(pdf_path, query)\n",
        "\n",
        "# 打印图增强RAG系统生成的响应\n",
        "print(\"\\n=== 答案 ===\")\n",
        "print(results[\"response\"])\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "正在从 data/AI_Information_zh.pdf 提取文本...\n",
            "创建了 13 个文本块\n",
            "正在构建知识图谱...\n",
            "正在为块创建嵌入...\n",
            "正在向图中添加节点...\n",
            "正在为块 1/13 提取概念...\n",
            "正在为块 2/13 提取概念...\n",
            "正在为块 3/13 提取概念...\n",
            "正在为块 4/13 提取概念...\n",
            "正在为块 5/13 提取概念...\n",
            "正在为块 6/13 提取概念...\n",
            "正在为块 7/13 提取概念...\n",
            "正在为块 8/13 提取概念...\n",
            "正在为块 9/13 提取概念...\n",
            "正在为块 10/13 提取概念...\n",
            "正在为块 11/13 提取概念...\n",
            "正在为块 12/13 提取概念...\n",
            "正在为块 13/13 提取概念...\n",
            "正在创建节点间的边...\n",
            "构建了包含 13 个节点和 1 条边的知识图谱\n",
            "\n",
            "\n",
            "=== 评估查询 1/1 ===\n",
            "查询: 相比于RNN，变换器如何处理序列数据？\n",
            "正在为查询遍历图: 相比于RNN，变换器如何处理序列数据？\n",
            "从 5 个节点开始遍历\n",
            "图遍历找到了 5 个相关块\n",
            "\n",
            "响应: 根据提供的上下文信息，文中并没有直接提到变换器（Transformer）与递归神经网络（RNN）在处理序列数据方面的对比。因此，无法从给定的信息中得出结论。\n",
            "\n",
            "不过，可以补充说明一下变换器和RNN在处理序列数据方面的一般区别：\n",
            "\n",
            "- **递归神经网络 (RNN)**：根据提供的信息，RNN设计用于处理连续数据，如文本和时间序列。它们具有反馈连接，可使信息随着时间的推移而持续存在。\n",
            "  \n",
            "- **变换器 (Transformer)**：虽然上下文中没有提到变换器的具体细节，但通常来说，变换器通过自注意力机制直接并行处理整个输入序列，而不是像RNN那样顺序地处理数据，这使得它在长序列处理上具有优势。\n",
            "\n",
            "如果需要更详细的对比信息，请提供更多的相关背景或直接查询变换器的相关内容。\n",
            "\n",
            "\n",
            "=== 评估摘要 ===\n",
            "图节点数: 13\n",
            "图边数: 1\n",
            "\n",
            "查询 1: 相比于RNN，变换器如何处理序列数据？\n",
            "路径长度: 5\n",
            "使用的块数: 5\n"
          ]
        }
      ],
      "source": [
        "def evaluate_graph_rag(pdf_path, test_queries, reference_answers=None):\n",
        "    \"\"\"\n",
        "    在多个测试查询上评估图增强RAG。\n",
        "    \n",
        "    参数:\n",
        "        pdf_path (str): PDF文档路径\n",
        "        test_queries (List[str]): 测试查询列表\n",
        "        reference_answers (List[str], 可选): 用于比较的参考答案\n",
        "        \n",
        "    返回:\n",
        "        Dict: 评估结果\n",
        "    \"\"\"\n",
        "    # 从PDF中提取文本\n",
        "    text = extract_text_from_pdf(pdf_path)\n",
        "    \n",
        "    # 将文本分割为块\n",
        "    chunks = chunk_text(text)\n",
        "    \n",
        "    # 构建知识图谱（对所有查询执行一次）\n",
        "    graph, embeddings = build_knowledge_graph(chunks)\n",
        "    \n",
        "    results = []\n",
        "    \n",
        "    for i, query in enumerate(test_queries):\n",
        "        print(f\"\\n\\n=== 评估查询 {i+1}/{len(test_queries)} ===\")\n",
        "        print(f\"查询: {query}\")\n",
        "        \n",
        "        # 遍历图以找到相关信息\n",
        "        relevant_chunks, traversal_path = traverse_graph(query, graph, embeddings)\n",
        "        \n",
        "        # 生成响应\n",
        "        response = generate_response(query, relevant_chunks)\n",
        "        \n",
        "        # 追加当前查询的结果\n",
        "        results.append({\n",
        "            \"query\": query,\n",
        "            \"response\": response,\n",
        "            \"traversal_path_length\": len(traversal_path),\n",
        "            \"relevant_chunks_count\": len(relevant_chunks)\n",
        "        })\n",
        "        \n",
        "        # 显示结果\n",
        "        print(f\"\\n响应: {response}\\n\")\n",
        "    \n",
        "    # 返回评估结果和图统计\n",
        "    return {\n",
        "        \"results\": results,\n",
        "        \"graph_stats\": {\n",
        "            \"nodes\": graph.number_of_nodes(),\n",
        "            \"edges\": graph.number_of_edges(),\n",
        "            \"avg_degree\": sum(dict(graph.degree()).values()) / graph.number_of_nodes()\n",
        "        }\n",
        "    }\n",
        "\n",
        "# 定义测试查询进行正式评估\n",
        "test_queries = [\n",
        "    \"相比于RNN，变换器如何处理序列数据？\"\n",
        "]\n",
        "\n",
        "# 使用测试查询对图增强RAG系统进行正式评估\n",
        "evaluation = evaluate_graph_rag(pdf_path, test_queries)\n",
        "\n",
        "# 打印评估摘要统计\n",
        "print(\"\\n=== 评估摘要 ===\")\n",
        "print(f\"图节点数: {evaluation['graph_stats']['nodes']}\")\n",
        "print(f\"图边数: {evaluation['graph_stats']['edges']}\")\n",
        "for i, result in enumerate(evaluation['results']):\n",
        "    print(f\"\\n查询 {i+1}: {result['query']}\")\n",
        "    print(f\"路径长度: {result['traversal_path_length']}\")\n",
        "    print(f\"使用的块数: {result['relevant_chunks_count']}\")\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "rag",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.11"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
